//参考 https://github.com/wintc23/js-tree-tool
import { isArray } from "@/utils/is";

const DEFAULT_CONFIG = {
  id: "id",
  children: "children",
  pid: "pid",
};

const getConfig = (config: any) => Object.assign({}, DEFAULT_CONFIG, config);

const tools = {
  //传入一项的value 获取索引
  findNodeIndex(tree: any[], value: any): any {
    for (let [index, node] of tree.entries()) {
      if (value == node.value) {
        return index;
      } else if (node.children) {
        const indexNo = tools.findNodeIndex(node.children, value);
        if (indexNo !== null) {
          return indexNo;
        }
      }
    }
    return null;
  },
  fromList(list: any, config: any = {}) {
    config = getConfig(config);
    const nodeMap = new Map(),
      result: never[] = [],
      { id, children, pid } = config;
    for (const node of list) {
      node[children] = node[children] || [];
      nodeMap.set(node[id], node);
    }
    for (const node of list) {
      const parent = nodeMap.get(node[pid]);
      (parent ? parent.children : result).push(node);
    }
    return result;
  },

  toList(tree: any, config: any = {}) {
    config = getConfig(config);
    const { children } = config,
      result = [...tree];
    for (let i = 0; i < result.length; i++) {
      if (!result[i][children] || !isArray(result[i][children])) continue;
      result.splice(i + 1, 0, ...result[i][children]);
    }
    return result;
  },

  findNode(tree: any, func: (arg0: any) => any, config: any = {}) {
    config = getConfig(config);
    const { children } = config,
      list = [...tree];
    for (let node of list) {
      if (func(node)) return node;
      node[children] && list.push(...node[children]);
    }
    return null;
  },

  findNodeAll(tree: any, func: (arg0: any) => any, config: any = {}) {
    config = getConfig(config);
    const { children } = config,
      list = [...tree],
      result: any = [];
    for (let node of list) {
      func(node) && result.push(node);
      node[children] && list.push(...node[children]);
    }
    return result;
  },

  findPath(tree: any, func: (arg0: any) => any, config: any = {}) {
    config = getConfig(config);
    const path: any = [],
      list = [...tree],
      visitedSet = new Set(),
      { children } = config;
    while (list.length) {
      const node = list[0];
      if (visitedSet.has(node)) {
        path.pop();
        list.shift();
      } else {
        visitedSet.add(node);
        node[children] && list.unshift(...node[children]);
        path.push(node);
        if (func(node)) return path;
      }
    }
    return null;
  },

  findPathAll(tree: any, func: (arg0: any) => any, config: any = {}) {
    config = getConfig(config);
    const path: any = [],
      list = [...tree],
      result: any = [];
    const visitedSet = new Set(),
      { children } = config;
    while (list.length) {
      const node = list[0];
      if (visitedSet.has(node)) {
        path.pop();
        list.shift();
      } else {
        visitedSet.add(node);
        node[children] && list.unshift(...node[children]);
        path.push(node);
        func(node) && result.push([...path]);
      }
    }
    return result;
  },

  filter(tree: any, func: (arg0: any) => any, config: any = {}) {
    config = getConfig(config);
    const { children } = config;

    function listFilter(list: any[]) {
      if (!isArray(list)) {
        return;
      }
      return list
        .map((node) => ({ ...node }))
        .filter((node) => {
          node[children] = node[children] && listFilter(node[children]);
          return func(node) || (node[children] && node[children].length);
        });
    }
    return listFilter(tree);
  },

  forEach(
    tree: any,
    func: (arg0: any, arg1: number) => void,
    config: any = {}
  ) {
    config = getConfig(config);
    const list = [...tree],
      { children } = config;
    for (let i = 0; i < list.length; i++) {
      func(list[i], i);
      list[i][children] && list.splice(i + 1, 0, ...list[i][children]);
    }
  },

  _insert(
    tree: any,
    node: any,
    targetNode: any,
    config: { children?: any },
    after: number
  ) {
    config = getConfig(config);
    const { children } = config;
    function insert(list: any[]) {
      let idx = list.indexOf(node);
      idx < 0
        ? list.forEach((n) => insert(n[children] || []))
        : list.splice(idx + after, 0, targetNode);
    }
    insert(tree);
  },

  insertBefore(tree: any, newNode: any, oldNode: any, config = {}) {
    tools._insert(tree, oldNode, newNode, config, 0);
  },

  insertAfter(tree: any, oldNode: any, newNode: any, config = {}) {
    tools._insert(tree, oldNode, newNode, config, 1);
  },

  removeNode(tree: any, func: (arg0: any) => any, config: any = {}) {
    config = getConfig(config);
    const { children } = config,
      list = [tree];
    while (list.length) {
      const nodeList = list.shift();
      const delList = nodeList.reduce(
        (r: any[], n: any, idx: any) => (func(n) && r.push(idx), r),
        []
      );
      delList.reverse();
      delList.forEach((idx: any) => nodeList.splice(idx, 1));
      const childrenList = nodeList
        .map((n: { [x: string]: any }) => n[children])
        .filter((l: string | any[]) => l && l.length);
      list.push(...childrenList);
    }
  },
  addParentId(tree: any, parentId = null) {
    if (!isArray(tree)) {
      return;
    }
    tree.forEach((node: any) => {
      node.parentId = parentId;
      if (node.children && node.children.length > 0) {
        tools.addParentId(node.children, node.id);
      }
    });
  },
};

export default tools;
